#define PREDEFINED_LAMPS_PATTERN 0

//pins 0 and 1 are used for serial communication over usb, right?
int switch0 = 2;
int switch1 = 3;
int switch2 = 4;
int switch3 = 5;
int switch4 = 6;
int switch5 = 13;

int switch_serial_data = 8;
int switch_shift_clock = 9;
int switch_register_clock = 10;

int serial_data = 11;
int shift_clock = 12;
int register_clock = 7;

boolean lamps[20][4];
boolean old_switches[8][6];
boolean switches[8][6];

//auxialiary globals
int i, j;
int column=0;
int block=0;
int switch_row=0;
int flash_counter = 0;
int temp=0;
int read_lamps = 0;

void send_bit(int value){
  digitalWrite(serial_data, value);    
  digitalWrite(shift_clock, HIGH);
  digitalWrite(shift_clock, LOW);    
}

void switch_send_bit(int value){
  digitalWrite(switch_serial_data, value);    
  digitalWrite(switch_shift_clock, HIGH);
  digitalWrite(switch_shift_clock, LOW);    
}

void setup()
{
  Serial.begin(19200);

  pinMode(switch0, INPUT);
  pinMode(switch1, INPUT);
  pinMode(switch2, INPUT);
  pinMode(switch3, INPUT);
  pinMode(switch4, INPUT);
  pinMode(switch5, INPUT);
  
  pinMode(serial_data, OUTPUT);
  pinMode(shift_clock, OUTPUT);
  pinMode(register_clock, OUTPUT);

  pinMode(switch_serial_data, OUTPUT);
  pinMode(switch_shift_clock, OUTPUT);
  pinMode(switch_register_clock, OUTPUT);

  for (i=0;i<20;i++)
  {
    for (j=0;j<4;j++)
    {
      lamps[i][j] = false;
    }
  }

  for (i=0;i<8;i++)
  {
    for (j=0;j<6;j++)
    {
      switches[i][j] = false;
      old_switches[i][j] = false;
    }
  }

}

bool lamps_update=false;

void loop()                     // run over and over again
{  
  byte read_value;

//send switches matrix updates

  for (j=0;j<6;j++){
    for (i=0;i<8;i++){
      if (old_switches[i][j] != switches[i][j]){
        Serial.write(switches[i][j] ? 'S' : 's');
        Serial.write(i*6+j);
      }
      old_switches[i][j] = switches[i][j];
    }
  }

#if LAMPS_EVENT_BASED
  if (Serial.available()){
    read_value=Serial.read();
    
    if (lamps_update){
      if (read_value=='0'){
        lamps_update=false;
      }

      if (read_value=='L'){
        i=Serial.read();
        lamps[i/4][i%4]=true;
      }
  
      if (read_value=='l'){
        i=Serial.read();
        lamps[i/4][i%4]=false;
      }
    } else {
      if (read_value=='U'){
        lamps_update=true;
      }
    }
  }
#else
  if (Serial.available()){
    read_value=Serial.read();

    if (read_lamps){
      for (i=0;i<8;i++){
        if (i<4)
          lamps[(read_lamps-1)*2][i] = (read_value & (1 << i));
        else
          lamps[(read_lamps-1)*2+1][i-4] = (read_value & (1 << i));
      }
      read_lamps++;
      if (read_lamps > 10) read_lamps = 0;
    } else {
      if (read_value=='L') read_lamps=1;
    }
  }
#endif

#if PREDEFINED_LAMPS_PATTERN
//just flash the lamps in predefined patterns
  if (flash_counter++ > 500){
    flash_counter = 0;
    temp=(temp+1)%(4*(19-9));

//one lamp at a time

    for (i=0;i<4;i++){
      for (j=0;j<20;j++){
        lamps[j][i] = (temp==(4*(j-9)+i));
      }
    }
  }
#endif //PREDEFINED_LAMPS_PATTERN

#define NUMBLOCKS 2

  column = (column+1)%4;
  if (column==0){
    block = (block+1)%NUMBLOCKS;
  }
  int col, row;
  //send new lamp data to the lamps board through serial interface


//As I have inverted the bit ordering when I design the pcb, the code gets a little bit complicated:

//first byte:
//rows 0 a 7 
  for (row=0;row<8;row++){
    send_bit ((block==((7-row)%NUMBLOCKS)) && lamps[7-row][column]);
  }

//second byte:
//rows 8 a 15
  for (row=0;row<8;row++){
    send_bit ((block==((15-row)%NUMBLOCKS)) && lamps[15-row][column]);
  }

//third byte:
//columns 0 a 3
  for (col=0;col<4;col++){
    send_bit ((3-col) == column);
  }

//rows 16 a 19
  for (row=0;row<4;row++){
    send_bit ((block==((19-row)%NUMBLOCKS)) && lamps[19-row][column]);
  }

  //update lamps by sending a clock signal to the registers
  digitalWrite(register_clock, HIGH);
  digitalWrite(register_clock, LOW);

//address switch matrix and read its status
  switches[switch_row][0] = (digitalRead(switch0)==HIGH);
  switches[switch_row][1] = (digitalRead(switch1)==HIGH);
  switches[switch_row][2] = (digitalRead(switch2)==HIGH);
  switches[switch_row][3] = (digitalRead(switch3)==HIGH);
  switches[switch_row][4] = (digitalRead(switch4)==HIGH);
  switches[switch_row][5] = (digitalRead(switch5)==HIGH);


//switch address
  switch_row = (switch_row+1)%8;
  for (i=0;i<8;i++){
    switch_send_bit ((7-i) == switch_row);
  }

  digitalWrite(switch_register_clock, HIGH);
  digitalWrite(switch_register_clock, LOW);

}

